﻿using System;
using System.Reflection;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using FlitBit.Core;
using FlitBit.Emit;

namespace FlitBit.IoC.Mvc
{
    /// <summary>
    /// Register in Global.asax -> ControllerBuilder.Current.SetControllerFactory(new IocEnabledMvcControllerFactory());
    /// </summary>
    public class IocEnabledMvcControllerFactory : DefaultControllerFactory, IControllerFactory
    {
        static readonly MethodInfo CreateNewMethod = typeof(IocEnabledMvcControllerFactory).MatchGenericMethod("CreateNew", BindingFlags.Instance | BindingFlags.NonPublic, 1, typeof(object));

        public override IController CreateController(RequestContext requestContext, string controllerName)
        {
            if (requestContext == null)
                throw new ArgumentNullException("requestContext");

            if (string.IsNullOrEmpty(controllerName))
                throw new ArgumentException("Controller Name is empty", "controllerName");

            var controllerType = GetControllerType(requestContext, controllerName);
            var controller = CreateInstance(requestContext, controllerType);
            return controller;
        }

        IController CreateInstance(RequestContext requestContext, Type controllerType)
        {
            if (controllerType == null)
                throw new HttpException(404, string.Format("Controller not found at {0}", requestContext.HttpContext.Request.Path));

            if (!typeof(IController).IsAssignableFrom(controllerType))
                throw new ArgumentException("Controller must inherit from Controller", "controllerType");

            var create = CreateNewMethod.MakeGenericMethod(controllerType);
            var controller = create.Invoke(this, new object[] { }) as IController;
            return controller;
        }

        T CreateNew<T>()
        {
            var currentFactory = FactoryProvider.Factory;
            
            return currentFactory.CanConstruct<T>() ?
                currentFactory.CreateInstance<T>() : default(T);
        }
    }
}